#include "cprint.hpp"
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

/**
 * @brief Construct a new cprint::cprint object
 * @param pbuffer format buffer ptr
 * @param bufferSize format buffer size
 * @param writeCallback write n data callback
 * @param qwriteCallback (DMA/IT)queue write n data callback (need exTxDone_callback())
 */
cprint::cprint( uint8_t* pbuffer,
                uint16_t bufferSize,
                pWrite_func_t writeCallback):
                write_callback(writeCallback)

{
    this->pBuffer = pbuffer;
    this->bufferSize = bufferSize;
    this->queue = NULL;
    this->writeWait = 0;
}

cprint::~cprint()
{
    
}

void cprint::queue_init(cQueue_t *pcQ,
                        uint8_t *pBuffer,
                        uint16_t size,
                        pWrite_func_t qwriteCallback)
{
    queue = pcQ;
    qwrite_callback = qwriteCallback;
    cQueue_Create_Static(pcQ, pBuffer, 1, size);
}

int cprint::format(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    int len = vsnprintf((char*)pBuffer, bufferSize, fmt, args);
    va_end(args);
    return len;
}

uint8_t *cprint::readBuffer(uint16_t size)
{
    if (size > bufferSize) return NULL;
    return pBuffer;
}

int cprint::print(const char *fmt, ...)
{
    int len;
    va_list args;
    va_start(args, fmt);
    len = vsnprintf((char*)pBuffer, bufferSize, fmt, args);
    va_end(args);
    return write_callback(pBuffer, len);
}

int cprint::print(const char *str)
{
    return write_callback((uint8_t*)str, strlen(str));
}

int cprint::print(const uint8_t *data, int dataSize)
{
    return write_callback(data, dataSize);
}

int cprint::print(void)
{
    if (queue == NULL) return 0;
    uint16_t len = getUsage();
    if (len == 0) return 0;
    pop(pBuffer, len);
    return write_callback(pBuffer, len);
}

int cprint::qprint(const char *fmt, ...)
{
    if (queue == NULL || qwrite_callback == NULL) return 0;
    int len;
    va_list args;
    va_start(args, fmt);
    len = vsnprintf((char*)pBuffer, bufferSize, fmt, args);
    va_end(args);
    return qprint(pBuffer, len);
}

int cprint::qprint(const char *str)
{
    if (queue == NULL || qwrite_callback == NULL) return 0;
    return qprint((uint8_t*)str, strlen(str));
}

int cprint::qprint(const uint8_t *data, int dataSize)
{
    if (queue == NULL || qwrite_callback == NULL) return 0;
    int len = qwrite_callback((uint8_t*)data, dataSize);
    if (len < dataSize)
    {
        // 队列发送数量不足，可能是队列发送繁忙中，写入队列等待发送
        if (cQueue_Pushs(queue, (uint8_t*)data, dataSize - len) == CQUEUE_OK)
        {
            return dataSize - len;
        }
        return 0;
    }
    return len;
}

int cprint::push(const char *fmt, ...)
{
    if (queue == NULL) return 0;
    int len;
    va_list args;
    va_start(args, fmt);
    len = vsnprintf((char*)pBuffer, bufferSize, fmt, args);
    va_end(args);
    if (cQueue_Pushs(queue, (uint8_t*)pBuffer, len) == CQUEUE_OK)
    {
        return len;
    }
    return 0;
}

int cprint::push(const char *str)
{
    if (queue == NULL) return 0;
    uint16_t len = strlen(str);
    if (cQueue_Pushs(queue, (uint8_t*)str, len) == CQUEUE_OK)
    {
        return len;
    }
    return 0;
}

int cprint::push(const uint8_t *data, int dataSize)
{
    if (queue == NULL) return 0;
    if (cQueue_Pushs(queue, (uint8_t*)data, dataSize) == CQUEUE_OK)
    {
        return dataSize;
    }
    return 0;
}

int cprint::pop(uint8_t *data, int dataSize)
{
    if (queue == NULL) return 0;
    uint16_t size = cQueue_Usage(queue);
    size = dataSize > size ? size : dataSize;

    if (cQueue_Pops(queue, (uint8_t*)data, size) == CQUEUE_OK)
    {
        return dataSize;
    }
    return 0;
}

int cprint::getSpare(void)
{
    return cQueue_Spare(queue);
}

int cprint::getUsage(void)
{
    return cQueue_Usage(queue);
}

void cprint::clear(void)
{
    if (queue == NULL) return;
    cQueue_Clear(queue);
}

/**
 * @brief 队列外部发送完成回调
 * @retval int 再次触发发送的数据量
 */
int cprint::exTxDone_callback(void)
{
    if (queue == NULL || qwrite_callback == NULL) return 0;
	uint8_t *pdata;
	uint16_t size;
    if (writeWait)
    {
        cQueue_Skip(queue, writeWait);
        writeWait = 0;
    }
	size = cQueue_GetReadPtrMargin(queue);
    if (size)
    {
        pdata = (uint8_t*)cQueue_GetReadAdr(queue);
        qwrite_callback(pdata, size);
        writeWait = size;
        return size;
    }
    return 0;
}
